/******************************************************************************************
文件名：L_GPIO_Peripherals.c
作者：狼狼    版本：V1.0    日期：2020.12.16
文件描述：
    对GPIO外设的操作。
版本信息：
    STv7-t版本。
历史修改记录：
1. 2021-9-9:V1.0 狼狼
从STv7-t存档中取出，为Loice Slave功能做修改。
2. 2022-5-11:V1.0 狼狼
代码公开。
******************************************************************************************/
//                                              ↓
//+------------------------------------------------------------------------------------------+
//|                                            前言
//+------------------------------------------------------------------------------------------+
//|      在嵌入式中，若系统提供/sysfs/class/gpio操作接口，则可以在用户空间直接访问独立的GPIO，
//|  但在Hi3518EV200中，官方的SDK Kernel并未提供该用户空间接口，因此操作GPIO的方式只能通过操作
//|  IO外设寄存器实现。
//|      shell下的devmem命令、海思himm工具和mpp的HI_MPI_SYS_SetReg函数和HI_MPI_SYS_GetReg函数
//|  均可实现对寄存器的读写操作，不过更为通用的操作则是通过映射/dev/mem实现对IO外设的操控。
//+------------------------------------------------------------------------------------------+
//|                                          文件说明
//+------------------------------------------------------------------------------------------+
//|      本文件实现了对博云开发板上的几种基于GPIO的外设的操控，具体包括：
//|  [输出方向控制]
//|    1. 红色指示灯开/关   GPIO8_0
//|        引脚复用         0x200f0100  0x01
//|        GPIO方向         0x201C0400  0x01
//|        输出高电平（灭） 0x201C0004  0x01
//|        输出低电平（亮） 0x201C0004  0x00
//|
//|    2. 绿色指示灯开/关   GPIO7_4
//|        引脚复用         0x200f00f0  0x01
//|        GPIO方向         0x201B0400  0x10
//|        输出高电平（灭） 0x201B0040  0x10
//|        输出低电平（亮） 0x201B0040  0x00
//|
//|    3. IR LED开/关       GPIO7_7
//|        引脚复用         0x200f00fc  0x01
//|        GPIO方向         0x201B0400  0x80
//|        输出高电平（灭） 0x201B0200  0x80
//|        输出低电平（亮） 0x201B0200  0x00
//|
//|    4. IR CUT控制        GPIO0_1
//|        引脚复用         0x200f0074  0x00
//|        GPIO方向         0x20140400  0x02
//|        输出高电平       0x20140008  0x02
//|        输出低电平       0x20140008  0x00
//|        *上升沿开启IRCUT
//|    
//|  [输入方向查询]
//|    1. 光敏二极管输入    GPIO7_6
//|        引脚复用         0x200f00f8 0x01
//|        GPIO方向         0x201B0400 0x00
//|        读值寄存器       0x201B0100 0x40
//|        *周围光线亮时读值为1，周围光线暗（夜晚）时读值为0
//|        *himm中可以通过写1查看当前寄存器值（该操作不影响输入电平）
//|
//|    2. 按键KEY输入       GPIO0_2
//|        引脚复用         0x200f0078 0x00
//|        GPIO方向         0x20140400 0x00
//|        读值寄存器       0x20140010 0x04
//|
//|    3. 自定义输入        GPIO7_5
//|        引脚复用         0x200f00f4 0x01
//|        GPIO方向         0x201B0400 0x00
//|        读值寄存器       0x201B0080 0x20
//|  
//|      在本程序中，初始化函数将上述GPIO初始化为固定的引脚复用和GPIO方向，每寄存器的操作均遵
//|  循读-改-写的方式，使用掩码控制修改的有效位。
//+------------------------------------------------------------------------------------------+
//|                                         头文件包含
//+------------------------------------------------------------------------------------------+
/*|*/#include <stdio.h>
/*|*/#include <stdlib.h>
/*|*/#include <unistd.h>
/*|*/#include <fcntl.h>
/*|*/#include <sys/mman.h>
//|
/*|*/#include "L_GPIO_Peripherals.h"
//+------------------------------------------------------------------------------------------+
//|                                         内置结构体
//+------------------------------------------------------------------------------------------+
//|    ----------------------------------------
//|    [说明]GPIO相关寄存器初始化值描述最小单元
//|    ----------------------------------------
/*|*/typedef struct
/*|*/{
/*|*/    unsigned long addr_base;
/*|*/    unsigned long addr_offset;
/*|*/    unsigned long value;
/*|*/    unsigned long mask;
/*|*/}ST_GPIO_32BIT_VALUE_LIST;
//|
/*|*/typedef struct
/*|*/{
/*|*/    unsigned long addr_base;
/*|*/    unsigned long addr_offset;
/*|*/    unsigned char value;
/*|*/    unsigned char mask;
/*|*/}ST_GPIO_8BIT_VALUE_LIST;
//+------------------------------------------------------------------------------------------+
//|                                          全局变量
//+------------------------------------------------------------------------------------------+
//|    ----------------------------------------
//|    [说明]基址-偏移-预计单个位写入值-掩码
//|          其中单个位写入值在写入期间仅用于
//|          0/1判断，具体写哪一位由掩码决定。
//|          程序会执行正确的位与或位或操作。
//|    ----------------------------------------
/*|*/static ST_GPIO_32BIT_VALUE_LIST gpio_32bit_value_list[]=
/*|*/{
//|      GPIO8_0引脚复用
/*|*/    {0x200f0000,0x00000100,0x00000001,0x00000001},//引脚复用寄存器仅最低位有效
//|      GPIO7_4引脚复用
/*|*/    {0x200f0000,0x000000f0,0x00000001,0x00000001},
//|      GPIO7_7引脚复用
/*|*/    {0x200f0000,0x000000fc,0x00000001,0x00000001},
//|      GPIO0_1引脚复用
/*|*/    {0x200f0000,0x00000074,0x00000000,0x00000001},
//|      GPIO7_6引脚复用
/*|*/    {0x200f0000,0x000000f8,0x00000001,0x00000001},
//|      GPIO0_2引脚复用
/*|*/    {0x200f0000,0x00000078,0x00000000,0x00000001},
//|      GPIO7_5引脚复用
/*|*/    {0x200f0000,0x000000f4,0x00000001,0x00000001},
//|
/*|*/    {0,0,0,0}
/*|*/};
//|
/*|*/static ST_GPIO_8BIT_VALUE_LIST gpio_8bit_value_list[]=
/*|*/{
//|      GPIO8_0输入/输出方向
/*|*/    {0x201C0000,0x00000400,0x01,0x01},
//|      GPIO7_4输入/输出方向
/*|*/    {0x201B0000,0x00000400,0x10,0x10},
//|      GPIO7_7输入/输出方向
/*|*/    {0x201B0000,0x00000400,0x80,0x80},
//|      GPIO0_1输入/输出方向
/*|*/    {0x20140000,0x00000400,0x02,0x02},
//|      GPIO7_6输入/输出方向
/*|*/    {0x201B0000,0x00000400,0x00,0x40},
//|      GPIO0_2输入/输出方向
/*|*/    {0x20140000,0x00000400,0x00,0x04},
//|      GPIO7_5输入/输出方向
/*|*/    {0x201B0000,0x00000400,0x00,0x20},
//|
/*|*/    {0,0,0,0}
/*|*/};
//|
/*|*/static unsigned char *paddr_gpio_led_r       = NULL;
/*|*/static unsigned char *paddr_gpio_led_g       = NULL;
/*|*/static unsigned char *paddr_gpio_led_ir      = NULL;
/*|*/static unsigned char *paddr_gpio_ircut       = NULL;
/*|*/static unsigned char *paddr_gpio_light_diode = NULL;
/*|*/static unsigned char *paddr_gpio_key         = NULL;
/*|*/static unsigned char *paddr_gpio_GPIO7_5     = NULL;
//+------------------------------------------------------------------------------------------+
//|  函数名称：L_GPIOs_Init
//|  功能描述：按照GPIO赋值表中的内容初始化GPIO的引脚复用和输入/输出方向。映射部分GPIO值寄存器。
//|  参数说明：无
//|  返回值说明：成功返回0
//|  备注：
//+------------------------------------------------------------------------------------------+
int L_GPIOs_Init(void)
{
    int i=0;
    int devfd=-1;
    
    devfd=open("/dev/mem",O_RDWR|O_SYNC);
    if(devfd<0)
    {
        printf("[GPIO-ERROR]Open /dev/mem failed!\n");
        return -1;
    }
    
    /*写32位寄存器*/
    unsigned long *preg32=NULL;
    while(gpio_32bit_value_list[i].addr_base!=0)
    {
        preg32=(unsigned long *)mmap(NULL,getpagesize(),PROT_READ|PROT_WRITE,MAP_SHARED,devfd,gpio_32bit_value_list[i].addr_base);
        
        if(gpio_32bit_value_list[i].value!=0)
        {
            //置位使用位或
            *(preg32+gpio_32bit_value_list[i].addr_offset)=(*(preg32+gpio_32bit_value_list[i].addr_offset))|(gpio_32bit_value_list[i].mask);
        }
        else
        {
            //清零使用位与
            *(preg32+gpio_32bit_value_list[i].addr_offset)=(*(preg32+gpio_32bit_value_list[i].addr_offset))&(~gpio_32bit_value_list[i].mask);
        }
        
        munmap(preg32,sizeof(unsigned long));
        i++;
    }
    
    /*写8位寄存器*/
    unsigned char *preg8=NULL;
    while(gpio_8bit_value_list[i].addr_base!=0)
    {
        preg8=(unsigned char *)mmap(NULL,getpagesize(),PROT_READ|PROT_WRITE,MAP_SHARED,devfd,gpio_8bit_value_list[i].addr_base);
        
        if(gpio_8bit_value_list[i].value!=0)
        {
            //置位使用位或
            *(preg8+gpio_8bit_value_list[i].addr_offset)=(*(preg8+gpio_8bit_value_list[i].addr_offset))|(gpio_8bit_value_list[i].mask);
        }
        else
        {
            //清零使用位与
            *(preg8+gpio_8bit_value_list[i].addr_offset)=(*(preg8+gpio_8bit_value_list[i].addr_offset))&(~gpio_8bit_value_list[i].mask);
        }
        
        munmap(preg8,sizeof(unsigned char));
        i++;
    }
    
    /*映射每个GPIO基址寄存器*/
    paddr_gpio_led_r       = (unsigned char *)mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, devfd, 0x201C0000);
    paddr_gpio_led_g       = (unsigned char *)mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, devfd, 0x201B0000);
    paddr_gpio_led_ir      = (unsigned char *)mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, devfd, 0x201B0000);
    paddr_gpio_ircut       = (unsigned char *)mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, devfd, 0x20140000);
    paddr_gpio_light_diode = (unsigned char *)mmap(NULL, getpagesize(), PROT_READ , MAP_SHARED, devfd, 0x201B0000);
    paddr_gpio_key         = (unsigned char *)mmap(NULL, getpagesize(), PROT_READ , MAP_SHARED, devfd, 0x20140000);
    paddr_gpio_GPIO7_5     = (unsigned char *)mmap(NULL, getpagesize(), PROT_READ , MAP_SHARED, devfd, 0x201B0000);
    
    close(devfd);
    return 0;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_GPIOs_Deinit
//|  功能描述：寄存器解映射
//|  参数说明：无
//|  返回值说明：无
//|  备注：
//+------------------------------------------------------------------------------------------+
void L_GPIOs_Deinit(void)
{
    munmap(paddr_gpio_led_r      , getpagesize());
    munmap(paddr_gpio_led_g      , getpagesize());
    munmap(paddr_gpio_led_ir     , getpagesize());
    munmap(paddr_gpio_ircut      , getpagesize());
    munmap(paddr_gpio_light_diode, getpagesize());
    munmap(paddr_gpio_key        , getpagesize());
    munmap(paddr_gpio_GPIO7_5    , getpagesize());
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_GPIO_LED_R_On
//|  功能描述：点亮红色LED
//|  参数说明：无
//|  返回值说明：无
//|  备注：
//+------------------------------------------------------------------------------------------+
void L_GPIO_LED_R_On(void)
{
    if(paddr_gpio_led_r!=NULL)
    {
        *(paddr_gpio_led_r+0x00000004)=0x00;
    }
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_GPIO_LED_R_Off
//|  功能描述：熄灭红色LED
//|  参数说明：无
//|  返回值说明：无
//|  备注：
//+------------------------------------------------------------------------------------------+
void L_GPIO_LED_R_Off(void)
{
    if(paddr_gpio_led_r!=NULL)
    {
        *(paddr_gpio_led_r+0x00000004)=0x01;
    }
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_GPIO_LED_G_On
//|  功能描述：点亮绿色LED
//|  参数说明：无
//|  返回值说明：无
//|  备注：
//+------------------------------------------------------------------------------------------+
void L_GPIO_LED_G_On(void)
{
    if(paddr_gpio_led_g!=NULL)
    {
        *(paddr_gpio_led_g+0x00000040)=0x00;
    }
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_GPIO_LED_G_Off
//|  功能描述：熄灭绿色LED
//|  参数说明：无
//|  返回值说明：无
//|  备注：
//+------------------------------------------------------------------------------------------+
void L_GPIO_LED_G_Off(void)
{
    if(paddr_gpio_led_g!=NULL)
    {
        *(paddr_gpio_led_g+0x00000040)=0x10;
    }
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_GPIO_LED_IR_On
//|  功能描述：点亮IR LED
//|  参数说明：无
//|  返回值说明：无
//|  备注：
//+------------------------------------------------------------------------------------------+
void L_GPIO_LED_IR_On(void)
{
    if(paddr_gpio_led_ir!=NULL)
    {
        *(paddr_gpio_led_ir+0x00000200)=0x80;
    }
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_GPIO_LED_IR_Off
//|  功能描述：熄灭IR LED
//|  参数说明：无
//|  返回值说明：无
//|  备注：
//+------------------------------------------------------------------------------------------+
void L_GPIO_LED_IR_Off(void)
{
    if(paddr_gpio_led_ir!=NULL)
    {
        *(paddr_gpio_led_ir+0x00000200)=0x00;
    }
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_GPIO_IRCUT_On
//|  功能描述：开启IRCUT
//|  参数说明：无
//|  返回值说明：无
//|  备注：IRCUT在控制引脚上升沿开启
//+------------------------------------------------------------------------------------------+
void L_GPIO_IRCUT_On(void)
{
    if(paddr_gpio_ircut!=NULL)
    {
        *(paddr_gpio_ircut+0x00000008)=0x02;
        *(paddr_gpio_ircut+0x00000008)=0x00;
    }
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_GPIO_IRCUT_Off
//|  功能描述：关闭IRCUT
//|  参数说明：无
//|  返回值说明：无
//|  备注：IRCUT在控制引脚下升沿关闭
//+------------------------------------------------------------------------------------------+
void L_GPIO_IRCUT_Off(void)
{
    if(paddr_gpio_ircut!=NULL)
    {
        *(paddr_gpio_ircut+0x00000008)=0x00;
        *(paddr_gpio_ircut+0x00000008)=0x02;
    }
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_GPIO_LIGHT_DIODE_STATE
//|  功能描述：读取光敏二极管输入
//|  参数说明：无
//|  返回值说明：返回电平状态，失败返回-1
//|  备注：周围光线亮时读值为1，周围光线暗（夜晚）时读值为0
//+------------------------------------------------------------------------------------------+
int L_GPIO_LIGHT_DIODE_STATE(void)
{
    if(paddr_gpio_light_diode!=NULL)
    {
        if(*(paddr_gpio_light_diode+0x00000100)&0x40)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
    
    return -1;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_GPIO_KEY_STATE
//|  功能描述：读取按键输入
//|  参数说明：无
//|  返回值说明：返回电平状态，失败返回-1
//|  备注：
//+------------------------------------------------------------------------------------------+
int L_GPIO_KEY_STATE(void)
{
    if(paddr_gpio_key!=NULL)
    {
        if(*(paddr_gpio_key+0x00000010)&0x04)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
    
    return -1;
}

//+------------------------------------------------------------------------------------------+
//|  函数名称：L_GPIO_7_5_STATE
//|  功能描述：读取GPIO7_5上的引脚电平
//|  参数说明：无
//|  返回值说明：返回电平状态，失败返回-1
//|  备注：
//+------------------------------------------------------------------------------------------+
int L_GPIO_7_5_STATE(void)
{
    if(paddr_gpio_GPIO7_5!=NULL)
    {
        if(*(paddr_gpio_GPIO7_5+0x00000080)&0x20)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
    
    return -1;
}
